# -*- coding: iso-8859-1 -*-


"""
Catanyste - Copyright 2008-2009 Julien RAIGNEAU
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance 
with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 
on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for 
the specific language governing permissions and limitations under the License. 

@author J.Raigneau
"""

from hexagone import Hexagone, Thief
from hexmanager import HexManager
from gamecomponents import GameComponents, ResourceFieldManager
from vertex import Vertex

class Board(object):
    """
    Main Data Model
    """

    def __init__(self, big_board=False):
        """
        Inits Board class
        """ 
        #TODO: Manage board size + options (number of player)
        self.big_board = big_board
        self.thief = Thief((99,99))
        self.hexmanager = HexManager((15,15)) # big enough for both 4- and 6-player board

        self.observersList = [] # list of board observers
#        self.attach_observer(observer)  # --> view must register to the data model, not the contrary

        self.resource_manager = ResourceFieldManager(self.big_board)
        self.__defHexes()     # hexes with resource and values
        
        #board is ready => need to notify observer
        self.notify_all_observers()
    
    def attach_observer(self, observer):
        """
        Adds observer
        """
        self.observersList.append(observer)

    # TODO: unattach_observer()
    def unattach_observer(self, observer):
        """
        Remove observer
        """
        pass
    
    def notify_all_observers(self):
        """
        Notifies all observers about board changes
        observer should implement update() function
        """
        for observer in self.observersList:
            observer.notify()

    def __defHexes(self):
        """
        define board's hexes
        """
        manager = self.resource_manager
        #hack manager while not using port3 and port2
#        manager.resource_fields[GameComponents.SEA_FIELDS][GameComponents.WATER] = 18 + 4
#        manager.resource_fields_ref[GameComponents.SEA_FIELDS][GameComponents.WATER] = 18 + 4
        #--end hack
        # constants to improve code readibiliy
        takeRand = getattr(manager,'takeRandomEarthField')
        takeWater = getattr(manager,'takeSemiRandomSeaField')


        # ok, i have a problem, here, i'd like something auto, but meanwile I can't design it
        # pb is: I must do all WATER together and in right order to use 'takeSemiRandomSeaField'

        if not self.big_board:
            earth_fields = ((2,0),(2,1),(2,2),(1,2),(0,2),(-1,1),(-2,0),(-2,-1),(-2,-2),(-1,-2),(0,-2),(1,-1),
                            (1,0),(1,1),(0,1),(-1,0),(-1,-1),(0,-1),
                            (0,0))
            sea_fields = ((3,0),(3,1),(3,2),(3,3),(2,3),(1,3),(0,3),(-1,2),(-2,1),(-3,0),(-3,-1),(-3,-2),(-3,-3),(-2,-3),(-1,-3),(0,-3),(1,-2),(2,-1))
        else:
            earth_fields = ((1,-2),(2,-1),(3,0),(3,1),(3,2),(3,3),(2,3),(1,3),(0,3),(-1,2),(-2,1),
                            (-2,0),(-2,-1),(-2,-2),(-1,-2),(0,-2),(1,-1),(2,0),(2,1),(2,2),(1,2),(0,2),(-1,1),
                            (-1,0),(-1,-1),(0,-1),(1,0),(1,1),(0,1),
                            (0,0))
            sea_fields = ((1,-3),(2,-2),(3,-1),(4,0),(4,1),(4,2),(4,3),(4,4),
                          (3,4),(2,4),(1,4),(0,4),(-1,3),(-2,2),(-3,1),(-3,0),
                          (-3,-1),(-3,-2),(-3,-3),(-2,-3),(-1,-3),(0,-3))

        # fill it with resources and value
        i = 0
        for uv in earth_fields:
            hexa = Hexagone(self, uv, takeRand(),99)
            if not hexa.get_resource() == Hexagone.DESERT:
                hexa.set_value(GameComponents.chips[i])
                i += 1
            else: self.putThief(hexa)
            self.hexmanager.AddHexagone(hexa)

        for uv in sea_fields:
            hexa = Hexagone(self, uv, takeWater(),99)
            self.hexmanager.AddHexagone(hexa)

#        ext = False
#        if nb_of_players > 4: ext = True
#
#        for u in range(-5, 5):
#            for v in range (-5, 5):
#                if not ext and hexacoord.distanceD6((u,v)) == 3:
#                    hexa = Hexagone(self, (u,v), takeWater(),99)
#                    self.hexmanager.AddHexagone(hexa)
#                elif ext and hexacoord.distanceD6((u,v)) == 3 and u <= 0 and v <= 0:
#                    hexa = Hexagone(self, (u,v), takeWater(),99)
#                    self.hexmanager.AddHexagone(hexa)
#                elif ext and hexacoord.distanceD6((u,v)) == 4 and (u > 0 or v > 0):
#                    hexa = Hexagone(self, (u,v), takeWater(),99)
#                    self.hexmanager.AddHexagone(hexa)
#                elif hexacoord.distanceD6((u,v)) <= 3:
#                    hexa = Hexagone(self, (u,v), takeRand(),99)
#                    if hexa.get_resource() == Hexagone.DESERT:
#                        self.putThief(hexa)
#                    self.hexmanager.AddHexagone(hexa)

        #we also need to define vertexes
        #ugly for now
        for hex in self.hexmanager.hexes:
            if hex:
                u0, v0 = hex.uv
                vertex1 = Vertex(self, (u0+1./3.,v0-1./3.))
                vertex2 = Vertex(self, (u0-1./3.,v0+1./3.))
                self.hexmanager.setVertex(vertex1)
                self.hexmanager.setVertex(vertex2)

        self.hexmanager.computeVertexValues()

#    def defValue(self, nb_of_players=4):
#        """
#        Put the value on the hexes
#        """
#        hexmanager = self.hexmanager
#        #I don't want to think for now
#        if nb_of_players <= 4:
#            for_chip = ((2,0),(2,1),(2,2),(1,2),(0,2),(-1,1),(-2,0),(-2,-1),(-2,-2),(-1,-2),(0,-2),(1,-1),
#                        (1,0),(1,1),(0,1),(-1,0),(-1,-1),(0,-1),
#                        (0,0))
#        else:
#            for_chip = ((1,-2),(2,-1),(3,0),(3,1),(3,2),(3,3),(2,3),(1,3),(0,3),(-1,2),(-2,1),
#                        (-2,0),(-2,-1),(-2,-2),(-1,-2),(0,-2),(1,-1),(2,0),(2,1),(2,2),(1,2),(0,2),(-1,1),
#                        (-1,0),(-1,-1),(0,-1),(1,0),(1,1),(0,1),
#                        (0,0))
#
#        i = 0
#        for uv in for_chip:
#            hex = hexmanager.GetHexagone(uv)
#            if hex:
#                if hex.get_resource() != Hexagone.DESERT:
#                    hex.set_value(GameComponents.chips[i])
#                    i += 1
#        #finally, compute value at vertex
#        hexmanager.computeVertexValues()

   
    def putThief(self, hex):
        """
        Puts thief on hex
        """
        self.thief.setThief(hex)
        if self.hexmanager: 
            self.hexmanager.computeVertexValues() #recompute values
            self.notify_all_observers()
         
    def getThief(self):
        """
        Returns thief coord
        """
        return self.thief.getCoord()
    
    def hasThief(self, hex):
        """
        Returns True if hex has Thief
        """
        return hex.hasthief
    
    #TODO: use __getItem and __setItem http://diveintopython.adrahon.org/object_oriented_framework/special_class_methods.html#d0e12873            
    #VGU: AMHA, we should not be able to get hex : leave it to hexmanager
    def getHexagoneFromCoord(self,hexCoord):
        """
        Gets hexagone from hexCoord
        """
        return self.hexmanager.GetHexagone(hexCoord)

    def get_hexagone_resource(self, uv):
        return self.hexmanager.GetHexagone(uv).get_resource()
    
    def update_hexagone_value(self,modHex,value):
        """
        updates hex value
        """
        modHex.set_value(value)
        self.update_hexagone(modHex)
        
    def update_hexagone_resource(self,modHex,resource):
        """
        updates hex resource
        """
        modHex.set_resource(resource)
        self.update_hexagone(modHex)
    
    def update_hexagone(self,modHex):
        """
        updates hexagone modHex
        """
        self.hexmanager.UpdateHexagone(modHex)
        self.hexmanager.computeVertexValues() #recompute values
        self.notify_all_observers()

    def get_vertex(self, uv):
        v = self.hexmanager.get_vertex(uv)
        if v:
            return (v.uv, v.esperance, dict(v.esperances), v.port)

    def get_all_hexes(self):
        hexes_info = []
        for h in self.hexmanager.hexes:
            if h:
                hexes_info.append((h.uv, h.resource, h.value, h.hasthief))
        return tuple(hexes_info)


    def get_all_vertexes(self):
        vertexes_info = []
        for v in self.hexmanager.vertexes1 + self.hexmanager.vertexes2:
            if v:
                vertexes_info.append((v.uv, v.esperance, dict(v.esperances)))
        return tuple(vertexes_info)

    def get_vertex_global_info(self):
        return Vertex.esperance, Vertex.esperances

    def loop2next_resource(self, uv):
        # reference list to loop through
        li = [Hexagone.DESERT,
              Hexagone.LUMBER,
              Hexagone.GRAIN,
              Hexagone.BRICK,
              Hexagone.ORE,
              Hexagone.WOOL]

        #which resource do we have now ?
        res = self.get_hexagone_resource(uv)
#        print 'found resource=%s' % res
        # put previous one in the list
        if res in li:
            i = li.index(res)
#        print 'change to resource=%s' % li[i+1-6]
            hex = self.getHexagoneFromCoord(uv)
            if hex:
                self.update_hexagone_resource(hex, li[i+1-6]) # -6 --> use negative value to prevent index overflow

    def set_value(self, uv, value):
        if value == 0 : value = 99
        hex = self.getHexagoneFromCoord(uv)
        if hex:
            if hex.get_resource() != Hexagone.SEA and \
               not hex.get_resource() == Hexagone.PORT3 and \
               not hex.get_resource() == Hexagone.PORT2LUMBER and \
               not hex.get_resource() == Hexagone.PORT2BRICK and \
               not hex.get_resource() == Hexagone.PORT2WOOL and \
               not hex.get_resource() == Hexagone.PORT2GRAIN and \
               not hex.get_resource() == Hexagone.PORT2ORE :
                self.update_hexagone_value(hex, value)

    def is_big(self):
        return self.big_board